/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is iRecipe code.
 *
 * The Initial Developer of the Original Code is Robert Morris.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */

package com.pavisoft.irecipe;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

/**
 * @author Robert Morris
 *
 * <code>RecipeLibrary</code> represents the collection of all <code>RecipeGroup</code>s
 * and the recipies contained within them. It also contains methods for reading
 * and writing relevant data to and from disk.
 */
public class RecipeLibrary
{
    private File libraryFile = new File("library.xml");
    private ArrayList<RecipeGroup> recipeGroups = new ArrayList<RecipeGroup>();
    private final String XML_FILE_VERSION = "0.1";
    
    /** Creates a new instance of RecipeLibrary.
     *  @param libraryFile Instance of <code>java.io.File</code> representing
     *  the path and filname of the library XML file.
     */
    public RecipeLibrary(File libraryFile)
    {
        this.libraryFile = libraryFile;
        this.readLibrary();
    }
    
    public RecipeLibrary(ArrayList<RecipeGroup> recipeGroups)
    {
        this.recipeGroups = recipeGroups;
    }
    
    public RecipeGroup[] getRecipeGroups()
    {
        return this.recipeGroups.toArray(new RecipeGroup[recipeGroups.size()]);
    }
    
    public void addRecipeGroup(RecipeGroup rg)
    {
        this.recipeGroups.add(rg);
    }
    
    public boolean removeRecipeGroup(RecipeGroup rg)
    {
        return this.recipeGroups.remove(rg);
    }
    
    /**
     *  Returns array of strings representing valid keywords for the
     *  recipes in the library (e.g., "vegetarian", "favorite", "sugar-free",
     *  etc.
     */
    public String[] getKeywords()
    {
        // TODO: Eventually make this a preference and store somewhere.
        // Let's hard-code for now just to get something going...
        return new String[] { "Favorite", "Vegetarian", "Low Calorie" };
    }
    
    // TODO: Implement complementary methods for adding, removing, and possibly
    // renaming keywords!
    
    public Element toXML()
    {
        Element library = new Element("library");
        library.setAttribute("version", XML_FILE_VERSION);
        Element groups = new Element("recipe-groups");
        for (RecipeGroup g : this.getRecipeGroups())
        {
            Element group = new Element("group");
            group.setAttribute("name", g.getName());
            for (Recipe r : g.getRecipes())
            {
                group.addContent(r.toXML());
            }
            groups.addContent(group);
        }
        library.addContent(groups);
        return library;        
    }
    
    /** Reads library from the XML file. */
    public void readLibrary()
    {
        SAXBuilder builder = new SAXBuilder();
        Document doc = null;
        this.recipeGroups = new ArrayList<RecipeGroup>();
        try
        {
            if (!(libraryFile.exists() && libraryFile.isFile()))
            {
                libraryFile.createNewFile();
            }
            doc = builder.build(libraryFile);
        }
        catch (Exception e)
        {            
            doc = new Document(new Element("library"));
        }
        
        Element library = doc.getRootElement();       
        List groups = null;
        
        try
        {
            groups = library.getChild("recipe-groups").getChildren("group");
        }
        catch (Exception e)
        {
            library.addContent(new Element("recipe-groups"));
        }
        
        if (groups != null)
        {
            for (Object child : groups)
            {
                Element group = (Element)child;
                RecipeGroup recipeGroup = new RecipeGroup(group.getAttributeValue("name"));
                for (Object recipeChild : group.getChildren("recipe"))
                {
                    Element recipeElem = (Element)recipeChild;
                    Element head;
                    String name;
                    String desc;
                    String preptime;
                    String yield;
                    ArrayList<String> keywords = new ArrayList<String>();
                    ArrayList<Ingredient> ingredients = new ArrayList<Ingredient>();
                    String directions;
                    Element kwds = null;
                    
                    head = recipeElem.getChild("head");
                    name = head.getChildText("title");
                    try
                    {
                        desc = head.getChildText("description");
                    } catch (Exception ex) {
                        desc = "";
                    }
                    try {
                        preptime = head.getChildText("preptime");
                    } catch (Exception ex) {
                        preptime = "";
                    }
                    try {
                        yield = head.getChildText("yield");
                    } catch (Exception ex) {
                        yield = "";
                    }
                    
                    try {
                        kwds = head.getChild("keywords");
                    } catch (Exception ex) {
                        kwds = new Element("keywords");
                    }                   
                    
                    if (kwds != null && kwds.getChild("keyword") != null)
                    {
                        for (Object kwd : kwds.getChildren("keyword"))
                        {
                            Element k = (Element)kwd;
                            keywords.add(k.getText());                        
                        }
                    }
                    
                    for (Object ingChild : recipeElem.getChild("ingredients").getChildren("ing"))
                    {
                        Element ingElem = (Element)ingChild;
                        String ingName;
                        Quantity qty;
                        Amount amt;
                        Amount.Unit unit;
                        Element amtElem = ingElem.getChild("amt");
                        qty = Quantity.parseQuantity(amtElem.getChildText("qty"));
                        unit = Amount.parseUnit(amtElem.getChildText("unit"));
                        amt = new Amount(qty, unit);
                        if (unit == Amount.Unit.OTHER)
                        {
                            amt.setUnknownUnitName(amtElem.getChildText("unit"));
                        }
                        ingName = ingElem.getChildText("item");
                        Ingredient ing = new Ingredient(ingName, amt);
                        ingredients.add(ing);                        
                    }
                    
                    directions = recipeElem.getChildText("directions");
                    Recipe recipe = new Recipe(name, ingredients, directions);
                    recipe.setDescription(desc);
                    for (String s : keywords)
                    {
                        recipe.addKeyword(s);
                    }
                    recipeGroup.addRecipe(recipe);
                    
                    if (yield != null)
                        recipe.setYield(yield);
                    if (preptime != null)
                        recipe.setPrepTime(preptime);
                }
                this.addRecipeGroup(recipeGroup);        
            }
       }      

    }
    
    /** Saves library to XML file. */
    public void saveLibrary()
    {
        Document libDoc = new Document(this.toXML());
        FileOutputStream fout = null;
        try
        {
            fout = new FileOutputStream(this.libraryFile);
        }
        catch (FileNotFoundException ex)
        {
            try
            {
                libraryFile.createNewFile();
                fout = new FileOutputStream(this.libraryFile);
            }
            catch (IOException exc)
            {
                System.err.println("Could not save library:\n\n" + exc.getMessage());
            }
        }
        
        if (!fout.equals(null))
        {
            XMLOutputter xo = new XMLOutputter();
            try
            {
                xo.output(libDoc, fout);
            }
            catch (IOException ex)
            {
                System.err.println("Could not write library file:\n\n" + ex.getMessage());
            }
        }
    }
    
    /** Moves the RecipeGroup (category) at <code>fromIndex</code> to <code>toIndex</code>.
     *  (Convienent for drag-and-drop GUIs.)
     *  @param fromIndex The current index of the category to move
     *  @param toIndex The index to which to move the category. (Note that this
     *   paramater reflects the index before modification of the categories; the
     *   actual resulting index may be different.)
     */
    public void moveRecipeGroup(int fromIndex, int toIndex)
    {
        if (fromIndex == toIndex) return;
        
        this.recipeGroups.add(toIndex, this.recipeGroups.get(fromIndex));
        
        if (fromIndex > toIndex)
        {
            this.recipeGroups.remove(fromIndex + 1);
        }
        else
        {
            this.recipeGroups.remove(fromIndex);
        }
    }
}
